package com.dys.service.impl;

import com.dys.common.ResponseResult;
import com.dys.entity.LoginUser;
import com.dys.entity.SysUser;
import com.dys.exception.CaptchaNotMatchException;
import com.dys.service.LoginService;
import com.dys.utils.Constants;
import com.dys.utils.JwtUtil;
import com.dys.utils.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @Author: 疾风
 * @ClassName: LoginServiceImpl
 * @Description: 登录实现类
 * @CreateDate: 2024/12/16
 * @Version: v1.0.0
 */
@Service
public class LoginServiceImpl implements LoginService {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisCache redisCache;

    @Override
    public ResponseResult login(SysUser sysUser) {
        // 1.调用AuthenticationManager的 authenticate方法,进行用户认证。
        // 1.1 需要传入一个Authentication对象的实现,该对象包含用户信息
        Authentication usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(sysUser.getUserName(), sysUser.getPassword());
        // 1.2 对Authentication和UserDetail进行匹配
        Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        // 2.如果认证没有通过,给出错误提示
        if (Objects.isNull(authenticate)) {
            throw new RuntimeException("登录失败");
        }
        // 3.如果认证通过,使用userId生成一个JWT,并将其保存到 ResponseResult对象中返回
        // 3.1 获取经过身份验证的用户的主体信息
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        // 3.2 获取到userID 生成JWT
        String userId = loginUser.getSysUser().getUserId().toString();
        String jws = JwtUtil.createJWT(userId);
        // 4.将用户信息存储在Redis中，在下一次请求时能够识别出用户,userid作为key
        redisCache.setCacheObject("login:" + userId, loginUser);

        // 5.封装ResponseResult,并返回
        Map<String, String> map = new HashMap<>(1);
        map.put("token", jws);
        return new ResponseResult(200, "登录成功", map);
    }

    @Override
    public ResponseResult logout() {
        // 1.获取当前用户的认证信息
        UsernamePasswordAuthenticationToken authentication =
                (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        if (Objects.isNull(authentication)) {
            throw new RuntimeException("获取用户认证信息失败,请重新登录");
        }
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        Long userId = loginUser.getSysUser().getUserId();
        // 2.删除redis中的认证信息
        String redisKey = "login:" + userId;
        redisCache.deleteObject(redisKey);
        return new ResponseResult(200, "登出成功", null);
    }

    @Override
    public String login(String username, String password, String code, String uuid) {
        // 1.从redis中获取验证码
        String verifyRedisKey = Constants.CAPTCHA_CODE_KEY + uuid;
        String captcha = redisCache.getCacheObject(verifyRedisKey);
        redisCache.deleteObject(captcha);

        if (captcha == null || !code.equalsIgnoreCase(captcha)) {
            throw new CaptchaNotMatchException("验证码错误!");
        }

        // 2.该方法会去调用UserDetailsServiceImpl.loadUserByUsername
        Authentication authenticate =
                authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));

        // 3.如果认证通过,使用userId生成一个JWT,并将其保存到 ResponseResult对象中返回
        // 3.1 获取经过身份验证的用户的主体信息
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();

        // 3.2 获取到userID 生成JWT
        String userId = loginUser.getSysUser().getUserId().toString();
        String jws = JwtUtil.createJWT(userId);

        // 4.将用户信息存储在Redis中，在下一次请求时能够识别出用户,userid作为key
        redisCache.setCacheObject("login:" + userId, loginUser);

        // 5.封装ResponseResult,并返回
        return jws;
    }
}
